/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007-2008 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.edition.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.Map.Entry;

import com.genia.toolbox.basics.exception.BundledException;
import com.genia.toolbox.constants.client.i18n.Messages;
import com.genia.toolbox.projects.toolbox_basics_project.bean.PersistableApplicationParameter;
import com.genia.toolbox.projects.toolbox_basics_project.bean.model.Language;
import com.genia.toolbox.projects.toolbox_basics_project.spring.manager.LanguageManager;
import com.genia.toolbox.projects.toolbox_basics_project.spring.manager.ParameterHandlerManager;
import com.genia.toolbox.projects.toolbox_basics_project.spring.manager.ParameterManager;
import com.genia.toolbox.projects.toolbox_basics_project.web.gwt.form.bean.FormParameterHandler;
import com.genia.toolbox.spring.provider.message.manager.CustomResourceBundleMessageSource;
import com.genia.toolbox.web.gwt.basics.client.i18n.GwtI18nMessage;
import com.genia.toolbox.web.gwt.basics.client.oracle.SimpleSuggestion;
import com.genia.toolbox.web.gwt.form.client.form.BasicForm;
import com.genia.toolbox.web.gwt.form.client.form.Form;
import com.genia.toolbox.web.gwt.form.client.form.MutableItem;
import com.genia.toolbox.web.gwt.form.client.form.VerticalForm;
import com.genia.toolbox.web.gwt.form.client.form.impl.LabelItemImpl;
import com.genia.toolbox.web.gwt.form.client.form.impl.VerticalFormImpl;
import com.genia.toolbox.web.gwt.form.client.value.DataIdentifier;
import com.genia.toolbox.web.gwt.form.client.value.FormValues;
import com.genia.toolbox.web.gwt.form.client.value.impl.DataIdentifierImpl;
import com.genia.toolbox.web.gwt.form.client.value.impl.FormValuesImpl;
import com.genia.toolbox.web.gwt.form.server.provider.FormProvider;

/**
 * {@link FormProvider} to handle edition of parameters.
 */
public class ParametersEditionFormProvider
    implements FormProvider

{

  /**
   * the {@link LanguageManager} to use.
   */
  private LanguageManager languageManager;

  /**
   * the forms to render depending on the {@link Locale}.
   */
  private transient final Map<Locale, BasicForm<Form>> formsByLocale = new HashMap<Locale, BasicForm<Form>>();

  /**
   * the form identifier.
   */
  private String formIdentifier;

  /**
   * the {@link ParameterManager} to use.
   */
  private ParameterManager parameterManager;

  /**
   * the {@link ParameterHandlerManager} containing the
   * {@link FormParameterHandler}s.
   */
  private ParameterHandlerManager formParameterHandlerManager;

  /**
   * the {@link CustomResourceBundleMessageSource} to use.
   */
  private CustomResourceBundleMessageSource messageSource;



  /**
   * get the display string for a given value.
   * 
   * @param fieldName
   *          the field name
   * @param value
   *          the current value of the field
   * @return the string to display for the given value
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.web.gwt.form.server.provider.FormProvider#getDisplayString(java.lang.String,
   *      java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public String getDisplayString(String fieldName, String value)
      throws BundledException
  {
    final PersistableApplicationParameter<?> applicationParameter = getParameterManager().getApplicationParameter(fieldName);
    if (applicationParameter == null) {
      return null;
    }
    FormParameterHandler<Object> formParameterHandler = (FormParameterHandler<Object>) getFormParameterHandlerManager().getHandler(applicationParameter);
    if (formParameterHandler == null) {
      return null;
    }
    return formParameterHandler.getDisplayString((PersistableApplicationParameter<Object>) applicationParameter, value);
  }



  /**
   * returns the form handled by this object.
   * 
   * @param dataIdentifier
   *          the data identifier
   * @return the form handled by this object
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.web.gwt.form.server.provider.FormProvider#getForm(String)
   */
  @SuppressWarnings("unchecked")
  public Form getForm(DataIdentifier dataIdentifier)
      throws BundledException
  {
    Language language = getLanguageManager().getCurrentLanguage();
    Locale currentLocale = null;
    if (language != null) {
      currentLocale = language.getLocale();
    }
    BasicForm<Form> form = formsByLocale.get(currentLocale);
    if (form == null) {
      Map<String, Map<String, List<PersistableApplicationParameter<?>>>> parametersByLabelAndCategory = new TreeMap<String, Map<String, List<PersistableApplicationParameter<?>>>>();
      for (PersistableApplicationParameter<?> applicationParameter : getParameterManager().getApplicationParameters()) {
        String category = getMessageSource().getMessage(applicationParameter.getCategory());
        Map<String, List<PersistableApplicationParameter<?>>> parametersByLabel = parametersByLabelAndCategory.get(category);
        if (parametersByLabel == null) {
          parametersByLabel = new TreeMap<String, List<PersistableApplicationParameter<?>>>();
          parametersByLabelAndCategory.put(category, parametersByLabel);
        }
        String label = getMessageSource().getMessage(applicationParameter.getName());
        List<PersistableApplicationParameter<?>> currentParameters = parametersByLabel.get(label);
        if (currentParameters == null) {
          currentParameters = new ArrayList<PersistableApplicationParameter<?>>();
          parametersByLabel.put(label, currentParameters);
        }
        currentParameters.add(applicationParameter);
      }
      form = new VerticalFormImpl();
      for (Entry<String, Map<String, List<PersistableApplicationParameter<?>>>> byCategoryParameterEntry : parametersByLabelAndCategory.entrySet()) {
        VerticalForm categoryForm = new VerticalFormImpl();
        LabelItemImpl categoryLabelItem = new LabelItemImpl();
        categoryLabelItem.setLabelKey(new GwtI18nMessage(Messages.SHOW_PARAMETER_KEY, new String[] { byCategoryParameterEntry.getKey() }));
        categoryForm.getForms().add(categoryLabelItem);

        form.getForms().add(categoryForm);

        for (Entry<String, List<PersistableApplicationParameter<?>>> byLabelParameterEntry : byCategoryParameterEntry.getValue().entrySet()) {
          for (PersistableApplicationParameter<?> applicationParameter : byLabelParameterEntry.getValue()) {
            categoryLabelItem.setName(applicationParameter.getCategory().getKey().replace('.', '_'));

            FormParameterHandler<Object> formParameterHandler = (FormParameterHandler<Object>) getFormParameterHandlerManager().getHandler(applicationParameter);
            final MutableItem item = formParameterHandler.getItem(byLabelParameterEntry.getKey(), (PersistableApplicationParameter<Object>) applicationParameter);
            item.setLabelKey(new GwtI18nMessage(Messages.SHOW_PARAMETER_KEY, new String[] { byLabelParameterEntry.getKey() }));
            item.setName(applicationParameter.getReferenceName());
            categoryForm.getForms().add(item);

          }
        }

      }

    }
    return form;
  }



  /**
   * get the string form identifier.
   * 
   * @return the string form identifier.
   * @see com.genia.toolbox.web.gwt.form.server.provider.FormProvider#getFormIdentifier()
   */
  public String getFormIdentifier()
  {
    return formIdentifier;
  }



  /**
   * retrieve the initial value of the object that is edited.
   * 
   * @param dataIdentifier
   *          the data identifier
   * @return the initial value of the object that is edited
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.web.gwt.form.server.provider.FormProvider#getInitialValue(java.lang.String)
   */
  @SuppressWarnings("unchecked")
  public FormValues getInitialValue(DataIdentifier dataIdentifier)
      throws BundledException
  {
    FormValuesImpl formValues = new FormValuesImpl();
    formValues.setFormIdentifier(getFormIdentifier());
    formValues.setDataIdentifier(new DataIdentifierImpl());
    for (PersistableApplicationParameter<?> applicationParameter : getParameterManager().getApplicationParameters()) {
      FormParameterHandler<Object> formParameterHandler = (FormParameterHandler<Object>) getFormParameterHandlerManager().getHandler(applicationParameter);
      formValues.addFormSimpleValue(formParameterHandler.getCurrentValue((PersistableApplicationParameter<Object>) applicationParameter));
    }
    return formValues;
  }



  /**
   * get suggestions for a field.
   * 
   * @param dataIdentifier
   *          the data identifier
   * @param fieldName
   *          the field name
   * @param query
   *          the current value of the field
   * @param limit
   *          the maximum number of response
   * @return the list of suggestion for the field
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.web.gwt.form.server.provider.FormProvider#getSuggestions(String,
   *      java.lang.String, java.lang.String, int)
   */
  @SuppressWarnings("unchecked")
  public List<SimpleSuggestion> getSuggestions(DataIdentifier dataIdentifier, String fieldName, String query, int limit)
      throws BundledException
  {
    final PersistableApplicationParameter<?> applicationParameter = getParameterManager().getApplicationParameter(fieldName);
    if (applicationParameter == null) {
      return null;
    }
    FormParameterHandler<Object> formParameterHandler = (FormParameterHandler<Object>) getFormParameterHandlerManager().getHandler(applicationParameter);
    if (formParameterHandler == null) {
      return null;
    }
    return formParameterHandler.getSuggestions((PersistableApplicationParameter<Object>) applicationParameter, query, limit);
  }



  /**
   * save the form on the server.
   * 
   * @param newValues
   *          the new values entered by the user
   * @return the new values that have been saved
   * @throws BundledException
   *           when an error occurred
   * @see com.genia.toolbox.web.gwt.form.server.provider.FormProvider#saveForm(com.genia.toolbox.web.gwt.form.client.value.FormValues)
   */
  @SuppressWarnings("unchecked")
  public FormValues saveForm(FormValues newValues)
      throws BundledException
  {
    for (PersistableApplicationParameter<?> applicationParameter : getParameterManager().getApplicationParameters()) {
      FormParameterHandler<Object> formParameterHandler = (FormParameterHandler<Object>) getFormParameterHandlerManager().getHandler(applicationParameter);
      formParameterHandler.setCurrentValue((PersistableApplicationParameter<Object>) applicationParameter, newValues.getFormSimpleValue(applicationParameter.getReferenceName()));
    }
    return newValues;
  }



  /**
   * getter for the languageManager property.
   * 
   * @return the languageManager
   */
  public LanguageManager getLanguageManager()
  {
    return languageManager;
  }



  /**
   * setter for the languageManager property.
   * 
   * @param languageManager
   *          the languageManager to set
   */
  public void setLanguageManager(LanguageManager languageManager)
  {
    this.languageManager = languageManager;
  }



  /**
   * setter for the formIdentifier property.
   * 
   * @param formIdentifier
   *          the formIdentifier to set
   */
  public void setFormIdentifier(String formIdentifier)
  {
    this.formIdentifier = formIdentifier;
  }



  /**
   * getter for the parameterManager property.
   * 
   * @return the parameterManager
   */
  public ParameterManager getParameterManager()
  {
    return parameterManager;
  }



  /**
   * setter for the parameterManager property.
   * 
   * @param parameterManager
   *          the parameterManager to set
   */
  public void setParameterManager(ParameterManager parameterManager)
  {
    this.parameterManager = parameterManager;
  }



  /**
   * getter for the formParameterHandlerManager property.
   * 
   * @return the formParameterHandlerManager
   */
  public ParameterHandlerManager getFormParameterHandlerManager()
  {
    return formParameterHandlerManager;
  }



  /**
   * setter for the formParameterHandlerManager property.
   * 
   * @param formParameterHandlerManager
   *          the formParameterHandlerManager to set
   */
  public void setFormParameterHandlerManager(ParameterHandlerManager formParameterHandlerManager)
  {
    this.formParameterHandlerManager = formParameterHandlerManager;
  }



  /**
   * getter for the messageSource property.
   * 
   * @return the messageSource
   */
  public CustomResourceBundleMessageSource getMessageSource()
  {
    return messageSource;
  }



  /**
   * setter for the messageSource property.
   * 
   * @param messageSource
   *          the messageSource to set
   */
  public void setMessageSource(CustomResourceBundleMessageSource messageSource)
  {
    this.messageSource = messageSource;
  }

}
